using CfgTable;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.Enum;
using NetProtocol.POD;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public class Level : BaseLevelEO
    {
        #region fields
        /// <summary>
        /// 实体id分配器
        /// </summary>
        private int _IdGenerator;
        /// <summary>
        /// 随机种子
        /// </summary>
        private int _initRandomSeed;
        /// <summary>
        /// 玩家集合
        /// </summary>
        private List<LevelPlayer> m_Players;
        public List<ISession> AllSessions { get; private set; }
        /// <summary>
        /// 地区
        /// </summary>
        private List<LevelRegion> m_Regions;
        private long _hostPlayerId;
        private bool _allPlayerEntered;
        private bool? _oldIsInTimePauseMode = null;
        #endregion fields

        #region properties
        /// <summary>
        /// 关卡id
        /// </summary>
        public long id { get; private set; }
        /// <summary>
        /// 是否本地服务器模式
        /// </summary>
        public bool isLocal { get; private set; }

        /// <summary>
        /// 关卡配置
        /// </summary>
        public CfgLevel cfgLevel { get; private set; }
        /// <summary>
        /// 关卡参数
        /// </summary>
        public Dictionary<int, int> levelParams { get; private set; }
        /// <summary>
        /// 当前逻辑帧
        /// </summary>
        public int levelFrame { get; private set; }
        /// <summary>
        /// 随机数生成器
        /// </summary>
        public FixedRandom random { get; private set; }
        /// <summary>
        /// 单机迷宫唯一的玩家或多人关卡房主。
        /// </summary>
        public LevelPlayer leaderPlayer => m_Players != null && m_Players.Count > 0 ? m_Players[0] : null;
        public List<LevelPlayer> players => m_Players;
        /// <summary>
        /// 视图层是否准备好关卡开始
        /// </summary>
        public bool isActive { get; private set; }
        /// <summary>
        /// 延迟服务管理器
        /// </summary>
        public ServiceDelayManager delayServiceMgr { get; private set; }
        /// <summary>
        /// 不存档延迟行为管理器
        /// </summary>
        public LgLevelDelayActionManager delayActionMgr { get; private set; }
        public List<LevelRegion> regions => m_Regions;
        /// <summary>
        /// 策划自定义关卡时间（24小时）
        /// 该值不为-1时，关卡时间以该值为准，否则以配置为准，配置为-1时以主城显示时间为准
        /// </summary>
        public int customTimeHour { get; private set; } = -1;
        /// <summary>
        /// 综合计算得出的当前小时（0-23）
        /// 优先看customTimeHour，如果是-1看配置，如果配置是-1，则返回主城时间
        /// </summary>
        public int curTimeHour => GetCurTimeHour();
        /// <summary>
        /// 存档pod，初始化关卡的各个步骤会用到，初始化完就无效了
        /// </summary>
        private LevelSavePOD savePod4Init { get; set; }

        #endregion properties

        public Level()
        {

        }

        public void Init(CreateLevelPOD levelPOD, LevelSavePOD levelSavePOD)
        {
            if (levelSavePOD != null && levelSavePOD.idGenerator != 0)
            {
                savePod4Init = levelSavePOD;
            }
            id = levelPOD.id;
            _initRandomSeed = levelPOD.randomSeed;
            random = new FixedRandom(_initRandomSeed);
            isLocal = levelPOD.isLocal;
            _hostPlayerId = levelPOD.hostPlayerId;
            cfgLevel = TableCenter.level.Get(levelPOD.cid);
            levelParams = new Dictionary<int, int>();
            m_Regions = new List<LevelRegion>();
            delayServiceMgr = new ServiceDelayManager();
            delayActionMgr = new LgLevelDelayActionManager();

            //先创建所有的玩家，但这时还没有完整数据
            //但后续需要再AddPlayer的地方判断玩家是否到齐，这里必须拥有完整的玩家列表
            m_Players = new List<LevelPlayer>();
            AllSessions = new List<ISession>();
            //先创建房主并放到第一个
            LevelPlayer player = ClassPool.Get<LevelPlayer>();
            player.InitId(_hostPlayerId, this);
            m_Players.Add(player);
            foreach (long pid in levelPOD.allPlayers)
            {
                if (pid != _hostPlayerId && pid != 0)
                {
                    player = ClassPool.Get<LevelPlayer>();
                    player.InitId(pid, this);
                    m_Players.Add(player);
                }
            }
        }

        public void Build()
        {
            for (int i = 0; i < cfgLevel.Regions.Count; i++)
            {
                int regionCid = cfgLevel.Regions[i];
                LevelRegionSavePOD regionSavePOD = null;
                savePod4Init?.regionSaves?.TryGetValue(regionCid, out regionSavePOD);
                LevelRegion region = ClassPool.Get<LevelRegion>();
                region.Init(regionCid, regionSavePOD, this, leaderPlayer);
                m_Regions.Add(region);
            }

            if (savePod4Init != null)
            {
                _IdGenerator = savePod4Init.idGenerator;
                random = new FixedRandom(savePod4Init.randomSeed);
                levelFrame = savePod4Init.levelFrame;
                customTimeHour = savePod4Init.timeHour;
                foreach (var kv in savePod4Init.levelParams)
                {
                    this.levelParams[kv.Key] = kv.Value;
                }
                foreach (var pod in savePod4Init.delayServices)
                {
                    LevelRegion region = GetRegion(pod.regionCid);
                    LevelEntity entity = pod.entityId == 0 ? null : region.GetEntityById(pod.entityId, true);
                    LevelServiceUnit serviceUnit = ClassPool.Get<LevelServiceUnit>();
                    region.gameplayConfig.services.TryGetValue(pod.serviceCid, out var serciceConfig);
                    LevelServiceUnitConfig unitConfig = serciceConfig.serviceUnits[pod.unitIndex];
                    serviceUnit.Init(pod.serviceCid, pod.unitIndex, unitConfig, entity);
                    delayServiceMgr.AddDelayServiceByDelayFrame(pod.leftFrame, pod.playerId == 0 ? null : GetPlayer(pod.playerId),
                        serviceUnit, (DelayServiceCondition)pod.conditionState, true, true);
                }
            }

            //初始化关卡参数默认值
            foreach (var cfgPlayEventParam in TableCenter.levelParam.DataList)
            {
                //仅在无存档时初始化
                //否则仅初始化离开关卡重置的参数
                if ((savePod4Init == null || cfgPlayEventParam.RecordMode == PlayEventParamRecordMode.ResetOnLeaveLevel) && cfgPlayEventParam.DefaultValue != 0)
                {
                    levelParams.TryAdd(cfgPlayEventParam.Id, cfgPlayEventParam.DefaultValue);
                }
            }

            foreach (LevelRegion region in regions)
            {
                region.Enable();
            }
        }

        /// <summary>
        /// 添加玩家，仅仅初始化尚不激活
        /// </summary>
        public LevelPlayer InitPlayer(LogicPlayer logicPlayer)
        {
            long playerId = logicPlayer.id;
            LevelPlayer player = GetPlayer(playerId);
            if (player == null)
            {
                return null;
            }
            LevelPlayerSavePOD playerSavePOD = null;
            savePod4Init?.playerSaves.TryGetValue(playerId, out playerSavePOD);
            player.InitByPOD(logicPlayer, playerSavePOD);
            return player;
        }

        /// <summary>
        /// 执行进入关卡（此后可发送消息给玩家）
        /// </summary>
        public void EnterLevel(LevelPlayer player, ISession session)
        {
            player.session = session;
            AllSessions.Add(session);
            LevelPlayerSavePOD playerSavePOD = null;
            savePod4Init?.playerSaves.TryGetValue(player.id, out playerSavePOD);

            //先处理玩家进入关卡的正常流程并激活玩家
            Enter(player, playerSavePOD == null ? 0 : playerSavePOD.regionCid);
            EnablePlayer(player);

            //如果有指定传送位置，则需再处理一次传送
            //必须先走完正常流程（传送到上一次存档的位置）再传送到指定位置，因为涉及读档
            int requestToTeleRegion = player.logicPlayer.playData.regionCid;
            int requestToTelePortalMcid = player.logicPlayer.playData.portalId;
            if (requestToTeleRegion != 0 || requestToTelePortalMcid != 0)
            {
                var regionCid = requestToTeleRegion != 0 ? requestToTeleRegion : player.region.CfgRegionInstance.Id;
                int tagOrMcid = requestToTelePortalMcid != 0 ? requestToTelePortalMcid : (int)LevelEntityTag.DefaultBorn;
                LevelEntity portal = GetEntityByTagOrMcid(regionCid, tagOrMcid);
                if (portal != null && LgLevelEntityUtil.GetTeleportPos(portal, out var position))
                {
                    EnterRegionAndTeleport(regionCid, player, position);
                }
            }

            //如果所有玩家都已就位，则广播进入关卡消息
            //否则等待最后一个玩家就位后广播
            TryBroadcastEnterLevel();
        }

        /// <summary>
        /// 玩家就位（进入关卡，进入指定地区），激活玩家角色
        /// </summary>
        private void EnablePlayer(LevelPlayer player)
        {
            Vector3Logic position = player.entity.position;
            if (player.checkPointId != 0)
            {
                if (player.region.GetTeleportPosByEntityId(player.checkPointId, out var teleportPos))
                {
                    position = teleportPos;
                }
            }
            else
            {
                if (player.region.GetTeleportPosByEntityTag((int)LevelEntityTag.DefaultBorn, out var teleportPos))
                {
                    position = teleportPos;
                }
            }

            //添加避障Agent，该Agent可产生类似阻挡的效果
            ObstacleInfo obstacle = new ObstacleInfo();
            obstacle.isStatic = false;
            obstacle.w = 30;
            obstacle.height = 100;
            var playerAvatar = player.entity;
            playerAvatar.ObstacleId = player.region.terrain.AddObstacle(position, Vector3Logic.forward, obstacle, playerAvatar.moveSpeed);
            //直接移动，不需要使用Teleport，因为前面获取position时已经通过寻路底层计算过合法位置了
            //同时上方添加Agent时也已经相当于同步过Agent的位置了
            playerAvatar.UpdateMove(position, 1, false, EntityMoveDirType.KeepVertical);

            //恢复跟随玩家实体的npc
            //AddPlayer -> Enter -> EnterRegion结束后，会根据存档数据创建entityFollowingMe引用，这里需要恢复跟随功能
            if (player.entityFollowingMe != null)
            {
                player.entityFollowingMe.FollowHero(player);
            }

            //执行手持道具效果
            LogicPlayer logicPlayer = player.logicPlayer;
            if (logicPlayer.quickBarItem != null)
            {
                LogicItemModule.Instance.TriggerPortableAddEffect(logicPlayer, logicPlayer.quickBarItem);
            }
        }

        public void TryBroadcastEnterLevel()
        {
            if (_allPlayerEntered)
            {
                return;
            }
            //是否所有玩家都已就位
            bool isAllInited = true;
            foreach (LevelPlayer p in m_Players)
            {
                if (p.session == null)
                {
                    isAllInited = false;
                    break;
                }
            }

            //如果所有玩家都已就位，向所有玩家发送EnterLevel的消息，否则忽略本次消息
            if (!isAllInited)
            {
                return;
            }

            foreach (LevelPlayer p in m_Players)
            {
                List<LevelEntityPOD> modifiedList = GetEntityPODsAfterEnterRegion(p);
                //发送通知
                EnterLevelPOD enterLevelPOD = GenEnterLevelPOD(p.id, modifiedList);
                SLevelLogicModule.NetCore.SC_NotifyEnter(p.session, enterLevelPOD);
            }

            _allPlayerEntered = true;
        }

        private List<LevelEntityPOD> GetEntityPODsAfterEnterRegion(LevelPlayer p)
        {
            //计算aoi实体，将aoi实体作为视图层关卡初始化实体
            p.region.CalcAOIEntities(p, true, out var modifiedList, out var removedList);

            //将当前玩家所属区域内的所有玩家添加到初始化实体列表里
            modifiedList = modifiedList ?? new List<LevelEntityPOD>();
            foreach (LevelPlayer regionPlayer in p.region.players)
            {
                modifiedList.Add(regionPlayer.entity.GeneratePOD() as LevelEntityPOD);
            }

            return modifiedList;
        }

        /// <summary>
        /// 保存
        /// </summary>
        /// <param name="player"></param>
        public bool Save(LevelPlayer player)
        {
            if (!CanSaveLevel())
            {
                LogicLog.LogError("存档错误：当前不能存档");
                return false;
            }
            player.SaveCheckPoint();
            InnerSave();
            return true;
        }

        private void InnerSave()
        {
            if (LogicHelper.SaveLevel != null)
            {
                LogicHelper.SaveLevel(leaderPlayer.id, id, GenerateSavePOD());
            }
        }

        public bool CanSaveLevel()
        {
            for (int i = 0; i < m_Regions.Count; i++)
            {
                LevelRegion region = m_Regions[i];
                if (!region.CanSave())
                {
                    return false;
                }
            }
            return true;
        }

        public void LogicOnUpdate()
        {
            if (!isActive)
            {
                return;
            }
            bool newIsInTimePauseMode = IsInTimePauseMode();
            if (_oldIsInTimePauseMode == null || newIsInTimePauseMode != _oldIsInTimePauseMode)
            {
                SwitchEntityBehaviour(newIsInTimePauseMode);
                _oldIsInTimePauseMode = newIsInTimePauseMode;
            }
            if (newIsInTimePauseMode)
            {
                return;
            }
            try
            {
                levelFrame++;

                for (int i = 0; i < m_Players.Count; i++)
                {
                    m_Players[i].LogicOnUpdate();
                }

                for (int i = 0; i < m_Regions.Count; i++)
                {
                    m_Regions[i].LogicOnUpdate();
                }

                delayServiceMgr.LogicOnUpdate();
                delayActionMgr.LogicOnUpdate();
            }
            catch (Exception e)
            {
                LogicLog.LogError(e);
            }
        }

        private void SwitchEntityBehaviour(bool isPause)
        {
            foreach (var region in regions)
            {
                region.SwitchEntityBehaviour(isPause);
            }
        }

        public LevelRegion GetRegion(int regionCid)
        {
            for (int i = 0; i < m_Regions.Count; i++)
            {
                LevelRegion region = m_Regions[i];
                if (region.CfgRegionInstance.Id == regionCid)
                {
                    return region;
                }
            }
            return null;
        }

        public LevelPlayer GetPlayer(long id)
        {
            for (int i = 0; i < m_Players.Count; i++)
            {
                LevelPlayer player = m_Players[i];
                if (player.id == id)
                {
                    return player;
                }
            }
            return null;
        }

        public bool HasPlayers()
        {
            return m_Players.Count > 0;
        }

        public int AllocateId()
        {
            return ++_IdGenerator;
        }

        private bool ValidateRegion(int regionCid)
        {
            CfgRegionInstance cfgRegion = TableCenter.regionInstance.Get(regionCid);
            if (cfgRegion == null)
            {
                return false;
            }
            return cfgLevel.Regions.IndexOf(regionCid) != -1;
        }

        public LevelEntity GetEntityById(int regionCid, int entityId)
        {
            var region = GetRegion(regionCid);
            return region?.GetEntityById(entityId);
        }

        public LevelEntity GetEntityByTagOrMcid(int regionCid, int tagOrMcid)
        {
            var region = GetRegion(regionCid);
            return region?.GetEntityByTagOrMcid(tagOrMcid);
        }

        private void EnterRegion(LevelRegion region, LevelPlayer player)
        {
            player.region?.ClearAllAOIEntitiesAndNotify(player);
            player.region?.RemovePlayer(player);
            //暂停关卡，等视图层加载和准备
            isActive = false;
            //玩家进入地区
            region.AddPlayer(player);
            player.OnEnterRegion(region);
        }

        public void EnterRegionAndTeleport(int regionCid, LevelPlayer player, Vector3Logic position)
        {
            if (player.region.CfgRegionInstance.Id != regionCid)
            {
                var region = GetRegion(regionCid);
                EnterRegion(region, player);
                region.Teleport(player, position);
                List<LevelEntityPOD> modifiedList = GetEntityPODsAfterEnterRegion(player);
                region.NotifyEnterRegion(player, modifiedList);
                region.TriggerEnter(player);
            }
            else
            {
                player.region.TeleportAndNotify(player, position);
            }
        }

        /// <summary>
        /// 进入关卡
        /// </summary>
        /// <param name="pid"></param>
        /// <param name="regionCid"></param>
        public void Enter(LevelPlayer player, int regionCid)
        {
            //如果玩家没有保存任何地区，则默认进入第一个地区
            if (regionCid == 0)
            {
                regionCid = cfgLevel.Regions[0];
            }
            if (!ValidateRegion(regionCid))
            {
                return;
            }
            LevelRegion region = GetRegion(regionCid);
            //进入地区
            EnterRegion(region, player);
            //根据存档恢复实体跟随玩家的关系
            if (savePod4Init != null && savePod4Init.playerSaves.TryGetValue(player.logicPlayer.id, out var playerSavePOD))
            {
                int followEntityId = playerSavePOD.followEntityId;
                LevelEntity followingEntity = region.GetEntityById(followEntityId);
                if (followingEntity != null)
                {
                    player.entityFollowingMe = followingEntity;
                }
            }
        }

        public bool IsAllPlayersReady()
        {
            bool isAllReady = true;
            for (int i = 0; i < m_Players.Count; i++)
            {
                if (!m_Players[i].isReadyToStart)
                {
                    isAllReady = false;
                    break;
                }
            }
            return isAllReady;
        }

        /// <summary>
        /// ready to start
        /// 激活关卡
        /// </summary>
        public void Active()
        {
            isActive = true;
            //所有玩家都
            foreach (LevelPlayer player in m_Players)
            {

            }
        }

        /// <summary>
        /// 进入区域
        /// </summary>
        /// <param name="regionCid"></param>
        /// <param name="player"></param>
        /// <param name="portalId"></param>
        /// <returns></returns>
        public int OnRequestEnterRegion(LevelPlayer player, int regionCid, int portalId)
        {
            if (!player.isInMainPhasing)
            {
                LogicLog.LogError("传送失败，该玩家不在主位面");
                return ErrorCode.InvalidStatus;
            }
            if (!ValidateRegion(regionCid))
            {
                return ErrorCode.InvalidRegion;
            }
            var portal = GetEntityById(regionCid, portalId);
            if (portal == null)
            {
                return ErrorCode.EntityNotFound;
            }
            if (!portal.isCheckPointActive)
            {
                return ErrorCode.InvalidPortal;
            }
            if (!LgLevelEntityUtil.GetTeleportPos(portal, out var position))
            {
                return ErrorCode.TargetPosInvalid;
            }
            EnterRegionAndTeleport(regionCid, player, position);
            return ErrorCode.SUCCESS;
        }

        /// <summary>
        /// 请求传送到实体附近
        /// </summary>
        /// <param name="player"></param>
        /// <param name="entityId"></param>
        /// <returns></returns>
        public TeleportResult OnRequestTeleport(LevelPlayer player, int entityId)
        {
            LevelEntity portal = player.region.GetEntityById(entityId);
            if (portal == null)
            {
                return TeleportResult.NotActiveCheckPoint;
            }
            if (!portal.isCheckPointActive)
            {
                return TeleportResult.NotActiveCheckPoint;
            }
            if (!LgLevelEntityUtil.GetTeleportPos(portal, out var position))
            {
                return TeleportResult.NavigationError;
            }
            bool isSuccess = player.region.TeleportAndNotify(player, position);
            return isSuccess ? TeleportResult.Success : TeleportResult.NavigationError;
        }

        /// <summary>
        /// 验证移动
        /// </summary>
        /// <param name="player"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public void Move(LevelPlayer player, Vector3Logic position)
        {
            player.region.Move(player, position);
        }

        /// <summary>
        /// 设置关卡参数
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetLevelParam(int key, int value, ExecChangeValueType type)
        {
            if (key <= 0 || TableCenter.levelParam.Get(key) == null)
            {
                LogicLog.LogError($"关卡参数id不合法：{key}");
                return;
            }
            if (type == ExecChangeValueType.Full)
            {
                levelParams[key] = value;
            }
            else
            {
                if (levelParams.TryGetValue(key, out var oldValue))
                {
                    levelParams[key] = oldValue + value;
                }
                else
                {
                    levelParams[key] = value;
                }
            }
        }

        public int GetLevelParam(int key)
        {
            if (levelParams.TryGetValue(key, out int value))
            {
                return value;
            }
            return 0;
        }

        /// <summary>
        /// 离开关卡
        /// </summary>
        /// <param name="player"></param>
        public void OnPlayerLeave(LevelPlayer player)
        {
            player.OnLeaveLevel();
            player.region.RemovePlayer(player);
            m_Players.Remove(player);
            AllSessions.Remove(player.session);
        }

        /// <summary>
        /// 英雄跳跃
        /// </summary>
        /// <param name="player"></param>
        /// <returns>ErrorCode</returns>
        public int HeroJump(LevelPlayer player, int entityId, float dirX, float dirZ)
        {
            Vector3Logic positionBefore = player.entity.position;
            int errorCode = player.region.HeroJump(player, entityId, dirX, dirZ);
            if (errorCode != ErrorCode.SUCCESS)
            {
                player.region.NotifySyncPosition(player, LevelMoveReason.JumpFail, LevelEntityType.Hero, player.entity, positionBefore, 1, EntityMoveDirType.KeepVertical);
            }
            return errorCode;
        }

        /// <summary>
        /// 切换物理/网格移动状态
        /// </summary>
        /// <param name="player"></param>
        /// <param name="isPhysical"></param>
        /// <returns></returns>
        public int SwitchPhysicNavMode(LevelPlayer player, bool isPhysical, float x, float y, float z)
        {
            int errorCode = player.region.SwitchPhysicNavMode(player, isPhysical, new Vector3Logic(x, y, z));
            if (errorCode != ErrorCode.SUCCESS)
            {
                LogicLog.LogError("切换寻路模式失败。ErrorCode：" + errorCode);
            }
            return errorCode;
        }

        /// <summary>
        /// 触发实体状态（大多数是陷阱）
        /// </summary>
        /// <param name="player"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public int OnTriggerInEntityState(LevelPlayer player, int entityId, int state)
        {
            return player.region.TriggerEntityState(player, entityId, state);
        }

        public void ChangeTime(int hour24)
        {
            this.customTimeHour = hour24;
        }

        /// <summary>
        /// 处理对话选项和对话结束
        /// </summary>
        /// <param name="player"></param>
        /// <param name="selectionIndexes">上报的对话配置上配置的选项列表里，玩家选择的选项索引</param>
        /// <returns></returns>
        public int OnRepoStoryDialogEnd(LevelPlayer player, List<int> selectionIndexes)
        {
            if (player.storyDialogCid == 0)
            {
                return ErrorCode.InvalidStatus;
            }

            //只有单人对话会设置占用状态，而多人剧情是纯观赏，不会有占用状态，也不会有服务，也不会有player.storyDialogCid数据
            //视图层如果上报，不处理后续逻辑
            //这段代码仅仅是专门针对多人模式对话的特殊处理
            LevelHeroEntity heroEntity = player.entity;
            if (!heroEntity.IsInOccupation(EntityOccupationStatus.Dialog))
            {
                return ErrorCode.SUCCESS;
            }

            //获取总表配置
            CfgStoryDialog cfgStoryDialog = TableCenter.storyDialog.Get(player.storyDialogCid);
            //完成任务目标 - 完成对话
            var tempParamList = ListPool<int>.Get();
            {
                tempParamList.Add(cfgStoryDialog.Id);
                LogicQuestModule.Instance.CheckTarget(player.logicPlayer, QuestTargetType.CompleteStory, tempParamList);
            }
            ListPool<int>.Put(tempParamList);

            //获取触发对话的实体（存在不经过特定实体触发对话的可能，即interactingEntity == null）
            LevelEntity interactingEntity = heroEntity.GetInteractingEntity(EntityOccupationStatus.Dialog) as LevelEntity;
            //释放占用
            heroEntity.ReleaseOccupation(EntityOccupationStatus.Dialog);
            interactingEntity?.ReleaseOccupation(EntityOccupationStatus.Dialog);

            //处理服务
            //有服务才处理
            var tbStory = LogicHelper.GetTbCfgStory(cfgStoryDialog.Name);
            if (tbStory.hasService)
            {
                //根据玩家的所有选项遍历整段对话，获取需要执行的服务
                List<int> toExecServices = ListPool<int>.Get();
                {
                    var node = tbStory.nodes[0];
                    int optionNodeIndex = 0;
                    //获取End之前所有的Option对应的服务
                    while (!node.customConfig.ContainsKey(EStoryNodeType.End))
                    {
                        int jumpId = -1;
                        if (node.customConfig.TryGetValue(EStoryNodeType.Option, out var vOption))
                        {
                            var options = (vOption as StoryNode_Option).choose;
                            int selectionIndex = selectionIndexes[optionNodeIndex];

                            var selectedOption = options[selectionIndex];
                            toExecServices.AddRange(selectedOption.service);

                            jumpId = selectedOption.jumpID;
                            optionNodeIndex++;
                        }
                        else //就跳转而言，只分选项跳转和直接跳转
                        {
                            jumpId = node.directJumpID;
                        }

                        node = tbStory.GetNode(jumpId);
                        if (node == null)
                        {
                            LogicLog.LogError($"根据jumpId {jumpId} 找不到下一个节点。（注意此时还没有到达剧情结束点。）");
                            return ErrorCode.Unknown;
                        }
                    }
                    //获取End的服务
                    if (node.customConfig.TryGetValue(EStoryNodeType.End, out var vEnd))
                    {
                        toExecServices.AddRange((vEnd as StoryNode_End).service);
                    }
                    //执行服务
                    if (toExecServices.Count > 0)
                    {
                        LevelServiceModule.TriggerSystemServices(player.region, player, interactingEntity, toExecServices);
                    }
                }
                //释放临时服务列表
                ListPool<int>.Put(toExecServices);
            }

            //清除玩家打开的对话数据
            player.storyDialogCid = 0;

            return ErrorCode.SUCCESS;
        }

        public bool IsInTimePauseMode()
        {
            if (m_Players.Count == 0)
            {
                return true;
            }
            if (m_Players.Count > 1) //多人迷宫不能全局时停, 可能所有玩家都离开了
                return false;
            LevelPlayer player = leaderPlayer;
            if (player.entity.IsInOccupation(EntityOccupationStatus.Dialog))
            {
                return true;
            }
            else if (player.entity.IsInOccupation(EntityOccupationStatus.Battle))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取地图显示需要的实体数据
        /// </summary>
        /// <param name="player"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public List<LevelEntityMapPOD> GetMapEntities(LevelPlayer player, int regionCid)
        {
            List<LevelEntityMapPOD> pods = new List<LevelEntityMapPOD>();
            var region = GetRegion(regionCid);
            if (region == null)
            {
                LogicLog.LogError($"找不到region cid：{regionCid}");
                return null;
            }
            foreach (var entity in region.entities)
            {
                if (entity.isAvailable && entity.entityConfig.mapUIEntityCid != 0)
                {
                    pods.Add(entity.GenerateMapPOD(player));
                }
            }
            return pods;
        }

        public void OnBattleEnd(LevelPlayer player, BattleResultType battleResult)
        {
            if (battleResult == BattleResultType.Success)
            {
                if (player.entity.TryGetOccupation(EntityOccupationStatus.Battle, out int monsterGroupId))
                {
                    var tempParamList = ListPool<int>.Get();
                    {
                        tempParamList.Add(monsterGroupId);
                        LogicQuestModule.Instance.CheckTarget(player.logicPlayer, QuestTargetType.FightMonsterWin, tempParamList);
                    }
                    ListPool<int>.Put(tempParamList);
                }
            }
            var ineteractEntity = player.entity.GetInteractingEntity(EntityOccupationStatus.Battle) as LevelEntity;
            player.entity.ReleaseOccupation(EntityOccupationStatus.Battle);
            ineteractEntity.ReleaseOccupation(EntityOccupationStatus.Battle);

            TrigValueTypeParam param = default;
            param.param1 = (int)battleResult;
            ineteractEntity.TryTriggerService(player, ServiceTriggerType.EndBattle, null, param);
            if (battleResult == BattleResultType.Failed)
            {
                ineteractEntity.ResetAI();
            }
        }

        /// <summary>
        /// 服务器通知队长变化
        /// </summary>
        /// <param name="pid"></param>
        /// <param name="leaderHeroCid"></param>
        public void OnLeaderChanged(long pid, long oldId, long leaderHeroId)
        {
            for (int i = 0; i < m_Regions.Count; i++)
            {
                LevelRegion region = m_Regions[i];
                var player = region.GetPlayer(pid);
                if (player != null)
                {
                    player.ChangeLeaderHero(leaderHeroId);
                    break;
                }
            }
        }

        public bool OnConfirmItemSubmit(LevelPlayer player, List<long> itemIdList, out bool isSuccess)
        {
            return player.region.ConfirmItemSubmit(player, itemIdList, out isSuccess);
        }

        private int GetCurTimeHour()
        {
            if (customTimeHour != -1)
            {
                return customTimeHour;
            }
            if (leaderPlayer == null || leaderPlayer.region == null)
            {
                return -1;
            }
            var cfgScene = TableCenter.scene.Get(leaderPlayer.region.CfgRegionInstance.SceneId);
            if (cfgScene.DefaultTime != -1)
            {
                return cfgScene.DefaultTime;
            }
            return DateTime.UnixEpoch.AddSeconds(leaderPlayer.logicPlayer.clientTime).Hour;
        }

        /// <summary>
        /// 实现的接口方法
        /// *注意返回的POD里没有Player的数据，需外部根据实际需求装填
        /// </summary>
        /// <returns></returns>
        public override IPOD GeneratePOD()
        {
            return null;
        }

        public LevelSavePOD GenerateSavePOD()
        {
            LevelSavePOD savePOD = new LevelSavePOD();
            savePOD.idGenerator = _IdGenerator;
            savePOD.randomSeed = random.seed;
            savePOD.levelFrame = levelFrame;
            savePOD.id = id;
            savePOD.cid = cfgLevel.Id;
            savePOD.hostPlayerId = _hostPlayerId;
            if (levelParams != null && levelParams.Count > 0)
            {
                Dictionary<int, int> toSaveLevelParams = new Dictionary<int, int>();
                foreach (var kv in levelParams)
                {
                    var cfgPlayEventParam = TableCenter.levelParam.Get(kv.Key);
                    if (cfgPlayEventParam.RecordMode != PlayEventParamRecordMode.ResetOnLeaveLevel)
                    {
                        toSaveLevelParams[kv.Key] = kv.Value;
                    }
                }
                savePOD.unsafe_levelParams = toSaveLevelParams;
            }
            foreach (var p in m_Players)
            {
                savePOD.playerSaves[p.logicPlayer.id] = p.GenerateSavePOD();
            }
            for (int i = 0; i < m_Regions.Count; i++)
            {
                LevelRegion region = m_Regions[i];
                LevelRegionSavePOD regionSave = new LevelRegionSavePOD();
                regionSave.unsafe_modifiedEntities = new List<LevelEntityPOD>(region.CreateToSaveEntityPODs());
                regionSave.unsafe_effectCids = region.effectIds?.Clone();
                regionSave.customBgmCid = region.customBgmCid;
                regionSave.unsafe_customWeathers = region.customWeathers?.Clone();
                savePOD.regionSaves[region.CfgRegionInstance.Id] = regionSave;
            }
            savePOD.unsafe_delayServices = delayServiceMgr.GeneratePOD();
            savePOD.timeHour = customTimeHour;
            return savePOD;
        }

        /// <summary>
        /// 创建进入关卡数据
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="aoiEntityPODs">注意该数据包括了aoi实体和所有玩家实体</param>
        /// <returns></returns>
        public EnterLevelPOD GenEnterLevelPOD(long playerId, List<LevelEntityPOD> aoiEntityPODs)
        {
            EnterLevelPOD pod = new EnterLevelPOD();
            LevelPlayer player = GetPlayer(playerId);
            pod.id = id;
            pod.cid = cfgLevel.Id;
            pod.player = player.GeneratePOD() as LevelPlayerPOD;
            foreach (LevelPlayer p in player.region.players)
            {
                if (p != player)
                {
                    pod.otherPlayers.Add(p.GenOtherPlayerPOD());
                }
            }
            pod.region = player.region.GeneratePOD() as LevelRegionPOD;
            pod.region.unsafe_entities = aoiEntityPODs;
            pod.unsafe_levelParams = levelParams?.Clone();
            pod.timeHour = customTimeHour;
            return pod;
        }

        public override void OnReset()
        {
            base.OnReset();
            foreach (var region in m_Regions)
            {
                ClassPool.Put(region);
            }
            m_Regions.Clear();
            m_Regions = null;
            foreach (var player in m_Players)
            {
                ClassPool.Put(player);
            }
            m_Players.Clear();
            m_Players = null;
            cfgLevel = null;
            isActive = false;
            delayServiceMgr.Dispose();
            delayServiceMgr = null;
            delayActionMgr.Dispose();
            delayActionMgr = null;
            _hostPlayerId = 0;
            _allPlayerEntered = false;
            _oldIsInTimePauseMode = null;
            _IdGenerator = 0;
            _initRandomSeed = 0;
            id = 0;
            isLocal = false;
            levelParams?.Clear();
            levelParams = null;
            levelFrame = 0;
            random = null;
            AllSessions.Clear();
            AllSessions = null;
            customTimeHour = -1;
            savePod4Init = null;
        }
    }
}
